package net.mohas.docsearch.controller;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.Writer;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.lucene.analysis.Analyzer;

import cern.colt.matrix.DoubleMatrix2D;
import cern.colt.matrix.linalg.Algebra;

/**
 *
 * @author Muhammad
 */
public class Searching {

    private String kueri;
    private DatabaseOperation odb;
    private TextOperation ot;
    private MatrixOperation om;
    private double threshold = 0.60;

    public Searching(TextOperation ot) {
        this.ot = ot;
        System.out.println("Kelas Pencarian(" + ot.getClass().getSimpleName() + ") terdefinisi.");
    }

    public String getKueri() {
        return kueri;
    }

    public void setKueri(String kueri) {
        this.kueri = kueri;
    }

    public double getThreshold() {
        return threshold;
    }

    public void setThreshold(double threshold) {
        this.threshold = threshold;
    }

    public void prosesKueri(String teks) throws Exception {
        odb = new DatabaseOperation();
        

        odb.hapusTabel("kueri");

        setKueri(teks);
        System.out.println("Memproses teks...");
        Analyzer an = ot.getAnalyzer();
        List<String> token = ot.tokenize(an, kueri);
        int jumlah = 0;
        for (String kata : token) {
            if (token.contains(kata)) {
                jumlah++;
            }
        }
        System.out.println("Jumlah: " + jumlah);
        Iterator<String> i = token.iterator();
        while (i.hasNext()) {
            String katanya = i.next().toString();
            System.out.println(katanya);
            System.out.println("Menghitung kata...");
            katanya = katanya.replace("\'", "");

            // cek apakah kata sudah ada dalam database.
            // kalau sudah, lakukan UPDATE
            // kalau belum, lakukan INSERT
            ResultSet result = odb.sqlSelect("SELECT kata,jumlah FROM kueri WHERE kata='" + katanya + "'");
            if (result.next()) {
                int jumKata = result.getInt("jumlah");
                jumKata++;

                odb.sqlUpdate("UPDATE kueri SET jumlah=" + jumKata + " WHERE kata='" + katanya + "'");
            } else {
                odb.sqlUpdate("INSERT INTO kueri VALUES ('" + katanya + "',1,0)");
            }
        }
        
    }

    public void hitungBobotKueri() throws Exception {
        odb = new DatabaseOperation();
        

        ResultSet result = odb.sqlSelect("SELECT SUM(jumlah) FROM kueri");
        System.out.println("SELECT SUM(jumlah) FROM kueri");
        int total = 0;
        if (result.next()) {
            total = result.getInt("SUM(jumlah)");
        }
        System.out.println("SUM(jumlah) = " + total + ", (total kata dalam kueri)");

        ResultSet result2 = odb.sqlSelect("SELECT kata,jumlah FROM kueri");
        System.out.println("SELECT kata,jumlah FROM kueri");
        int i = 0;
        String kata;
        int jumlah;
        double tf = 0;
        while (result2.next()) {
            i++;
            kata = result2.getString("kata");
            jumlah = result2.getInt("jumlah");
            tf = (double) jumlah / total;

            System.out.println("Kata ke " + i + " : " + kata + ", jumlah = " + jumlah);
            System.out.println("jumlah / total = " + jumlah + "/" + total + " = " + tf);
            System.out.println("tf : " + tf);

            ResultSet result4 = odb.sqlSelect("SELECT kata,idf FROM df where kata='" + kata + "'");
            System.out.println("SELECT kata,idf FROM df WHERE kata=" + kata);
            while (result4.next()) {
                // String katadf = result4.getString("kata");
                double idf = result4.getFloat("idf");
                double bobot = tf * idf;

                System.out.println("idf(" + kata + ") : " + idf);

                odb.sqlUpdate("UPDATE kueri SET bobot=" + bobot + " WHERE kata='" + kata + "'");
                System.out.println("UPDATE kueri SET bobot=" + bobot + " WHERE kata=" + kata);
            }
            result4.close();
        }
        result2.close();
        
    }

    public void buatMatriksKueri() throws Exception {
        File file = new File("src/thesis/files/pseudo-document.matrix");

        odb = new DatabaseOperation();
        

        ResultSet result = odb.sqlSelect("SELECT kata FROM df");
        System.out.println("SELECT kata FROM df");

        Writer output = new BufferedWriter(new FileWriter(file));
        // int i = 0;
        String kata;
        while (result.next()) {
            kata = result.getString("kata");

            ResultSet result2 = odb.sqlSelect("SELECT bobot FROM kueri WHERE kata='" + kata + "'");
            System.out.print("SELECT bobot FROM kueri WHERE kata=" + kata);

            if (result2.next()) {
                double bobot = result2.getDouble("bobot");
                System.out.println(" (" + String.format("%1.16f", bobot) + ")");
                output.append(String.format("%1.16f", bobot) + "\n");
            } else {
                System.out.println(" (" + String.format("%1.16f", 0.0) + ")");
                output.append(String.format("%1.16f", 0.0) + "\n");
            }
            result2.close();
        }
        result.close();
        output.close();
        
    }

    public DoubleMatrix2D hitungVektorKueri() throws Exception {
        om = new MatrixOperation();

        DoubleMatrix2D pd = om.getMatriksPseudoDoc();
        om.cetakMatriks(pd, "Matriks Pseudo-Document");

        DoubleMatrix2D lowRankU = om.getReducedU();
        DoubleMatrix2D lowRankS = om.getReducedS();

        Algebra aljabar = new Algebra();
        DoubleMatrix2D vektorKueri = aljabar.mult(aljabar.mult(om.transpose(pd), lowRankU), om.inverse(lowRankS));
        om.cetakMatriks(vektorKueri, "Vektor kueri");

        return vektorKueri;
    }

    public DoubleMatrix2D cosineSimilarity(DoubleMatrix2D vektorKueri, DoubleMatrix2D vektorDokumen) {
        Algebra aljabar = new Algebra();
        DoubleMatrix2D dotProduct = aljabar.mult(vektorKueri, vektorDokumen);
        DoubleMatrix2D cossim = dotProduct;
        double skor = 0;
        System.out.print("Dok :");
        for (int i = 0; i < dotProduct.columns(); i++) {
            System.out.printf("%10d", i + 1);
        }
        System.out.println();
        for (int baris = 0; baris < dotProduct.rows(); baris++) {
            System.out.print("     ");
            for (int kolom = 0; kolom < dotProduct.columns(); kolom++) {
                double euclideanDistance = aljabar.normF(vektorKueri)
                        * aljabar.normF(aljabar.subMatrix(vektorDokumen, 0, vektorKueri.columns() - 1, kolom, kolom));
                skor = (double) dotProduct.get(baris, kolom) / euclideanDistance;
                System.out.printf("%10.5f", skor);
                cossim.set(baris, kolom, skor);
            }
            System.out.println();
        }

        return cossim;
    }

    public List<HasilCari> urutkanHasil(DoubleMatrix2D similarity) throws Exception {

        // Mengambil nilai matriks similarity dan nomor berkasnya
        // Kemudian menyimpannya ke dalam HashMap
        final Map<String, Double> similarityMap = new HashMap<String, Double>();
        for (int i = 0; i < similarity.rows(); i++) {
            for (int j = 0; j < similarity.columns(); j++) {
                if (similarity.get(i, j) > 0.0D) {
                    similarityMap.put(Integer.toString((j + 1)), similarity.get(i, j));
                }
            }
        }

        // Mengurutkan similarityMap berdasarkan skornya
        // Kemudian menyimpan nomor berkas dan skor yang memiliki nilai di atas threshold
        List<HasilCari> hasilcari = new ArrayList<HasilCari>();
        List<String> nomorberkas = new ArrayList<String>();
        nomorberkas.addAll(similarityMap.keySet());

        Collections.sort(nomorberkas, new Comparator<String>() {

            public int compare(String s1, String s2) {
                return similarityMap.get(s2).compareTo(similarityMap.get(s1));
            }
        });

        for (String nomor : nomorberkas) {
            double nilai = similarityMap.get(nomor);
            if (nilai < threshold) {
                continue;
            }
            hasilcari.add(new HasilCari(nomor, nilai));
        }

        return hasilcari;
    }

    public void cetakHasil(List<HasilCari> hasilcari) {
        for (HasilCari hasil : hasilcari) {
            System.out.printf("%s (skor = %10.5f)\n", hasil.nomorberkas, hasil.skor);
        }
    }

    /*
    private String getIdBerkas(Map<String, Double> results) {

        String idberkas = "";
        List<String> nomorberkas = new ArrayList<String>();
        nomorberkas.addAll(results.keySet());
        for (String nomor : nomorberkas) {
            //System.out.println("idberkas.concat(result.id) : " + idberkas.concat(result.id));
            idberkas = idberkas.concat(nomor);
            //System.out.println("idberkas.concat(\",\") : " + idberkas.concat(","));
            idberkas = idberkas.concat(",");
        }
        return idberkas;
    }

    private String getSkorBerkas(Map<String, Double> results) {

        String skors = "";
        List<String> nomorberkas = new ArrayList<String>();
        nomorberkas.addAll(results.keySet());
        for (String nomor : nomorberkas) {
            //System.out.println("skors.concat(result.skor) : " + skors.concat(String.valueOf(String.format("%1.4f", result.skor))));
            skors = skors.concat(String.valueOf(String.format("%1.4f", results.get(nomor))));
            //System.out.println("skors.concat(\",\") : " + skors.concat(","));
            skors = skors.concat(",");
        }
        return skors;
    }*/

    public Map<String, String> cetakHasilDb(List<HasilCari> hasilcari) throws Exception {
        odb = new DatabaseOperation();
        

        Map<String, String> listHasil = new LinkedHashMap<String, String>();

        for (HasilCari hasil : hasilcari) {
            String id = hasil.nomorberkas;
            double skor = hasil.skor;

            ResultSet rs = odb.sqlSelect("SELECT namaberkas, lokasiberkas FROM berkas WHERE idberkas=" + id);
            while (rs.next()) {
                String nama = rs.getString("namaberkas");
                String lokasi = rs.getString("lokasiberkas");
                String teksList = "<p>" + nama + "<br>" + lokasi + "<br>Score: <b>" + String.format("%1.5f", skor) + "</b></p></td></tr></table></html>";
                
                if (nama.toLowerCase().endsWith(".txt")){
                    listHasil.put(lokasi, "<html><table border=\"0\"><tr><td><img src=\"file:src/thesis/files/image/icon/txt.png\"></td><td>" + teksList);
                } else if (nama.toLowerCase().endsWith(".rtf")){
                    listHasil.put(lokasi, "<html><table border=\"0\"><tr><td><img src=\"file:src/thesis/files/image/icon/txt.png\"></td><td>" + teksList);
                } else if (nama.toLowerCase().endsWith(".pdf")){
                    listHasil.put(lokasi, "<html><table border=\"0\"><tr><td><img src=\"file:src/thesis/files/image/icon/pdf.png\"></td><td>" + teksList);
                } else if (nama.toLowerCase().endsWith(".odt")){
                    listHasil.put(lokasi, "<html><table border=\"0\"><tr><td><img src=\"file:src/thesis/files/image/icon/odt.png\"></td><td>" + teksList);
                } else if (nama.toLowerCase().endsWith(".ods")){
                    listHasil.put(lokasi, "<html><table border=\"0\"><tr><td><img src=\"file:src/thesis/files/image/icon/ods.png\"></td><td>" + teksList);
                } else if (nama.toLowerCase().endsWith(".odp")){
                    listHasil.put(lokasi, "<html><table border=\"0\"><tr><td><img src=\"file:src/thesis/files/image/icon/odp.png\"></td><td>" + teksList);
                } else if (nama.toLowerCase().endsWith(".doc")){
                    listHasil.put(lokasi, "<html><table border=\"0\"><tr><td><img src=\"file:src/thesis/files/image/icon/doc.png\"></td><td>" + teksList);
                } else if (nama.toLowerCase().endsWith(".xls")){
                    listHasil.put(lokasi, "<html><table border=\"0\"><tr><td><img src=\"file:src/thesis/files/image/icon/xls.png\"></td><td>" + teksList);
                } else if (nama.toLowerCase().endsWith(".ppt")){
                    listHasil.put(lokasi, "<html><table border=\"0\"><tr><td><img src=\"file:src/thesis/files/image/icon/ppt.png\"></td><td>" + teksList);
                } else if (nama.toLowerCase().endsWith(".docx")){
                    listHasil.put(lokasi, "<html><table border=\"0\"><tr><td><img src=\"file:src/thesis/files/image/icon/doc.png\"></td><td>" + teksList);
                } else if (nama.toLowerCase().endsWith(".xlsx")){
                    listHasil.put(lokasi, "<html><table border=\"0\"><tr><td><img src=\"file:src/thesis/files/image/icon/xls.png\"></td><td>" + teksList);
                } else if (nama.toLowerCase().endsWith(".pptx")){
                    listHasil.put(lokasi, "<html><table border=\"0\"><tr><td><img src=\"file:src/thesis/files/image/icon/ppt.png\"></td><td>" + teksList);
                } else if (nama.toLowerCase().endsWith(".htm") || nama.toLowerCase().endsWith(".html")){
                    listHasil.put(lokasi, "<html><table border=\"0\"><tr><td><img src=\"file:src/thesis/files/image/icon/html.png\"></td><td>" + teksList);
                }

            }
        }
        
        return listHasil;
    }

    /*
    public void tampilkanHasil(Map<String, Double> results, String katakunci) throws Exception {

        String idberkas = getIdBerkas(results);
        String skors = getSkorBerkas(results);

        Desktop dekstop = Desktop.getDesktop();

        System.out.println("idberkas : " + idberkas);
        System.out.println("skors : " + skors);

        URI uri = new URI("http://localhost/tugasakhir/?ids=" + idberkas + "&skors=" + skors + "&q=" + katakunci.replace(" ", "+"));
        dekstop.browse(uri);
    }*/

    public class HasilCari {

        private String nomorberkas;
        private double skor;

        public HasilCari(String nomorberkas, double skor) {
            this.nomorberkas = nomorberkas;
            this.skor = skor;
        }

        public String getNomorberkas() {
            return nomorberkas;
        }

        public void setNomorberkas(String nomorberkas) {
            this.nomorberkas = nomorberkas;
        }

        public double getSkor() {
            return skor;
        }

        public void setSkor(double skor) {
            this.skor = skor;
        }
    }
	
}
